//借助select 解决盲等的问题
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <poll.h>


#define TTY1  "/dev/tty11"
#define TTY2  "/dev/tty12"
#define BUFSIZE 1024

//设计当前操作的数据结构
enum {
    STATE_R = 1,     //读
    STATE_W,         //写
    STATE_AUTO,      //状态分隔
    STATE_Ex,        //异常
    STATE_T          //终止？
};
struct fsm_st
{
    /* data */
    int state;  //当前状态机的状态
    int sfd;
    int dfd;
    char buf[BUFSIZE];
    int len;
    int pos;   //记录读取到的字符是否完全写入
    char *errstr ;   // 记录出错的原因
};

static void fsm_driver (struct fsm_st *fsm){
    int ret;
    switch(fsm->state){
        case STATE_R:         //读态，要做的操作
            fsm->len = read(fsm->sfd,fsm->buf,BUFSIZ);  //读数据
            if(fsm->len == 0){                          //没有数据，跳到终止态
                fsm->state = STATE_T;
            }
            else if(fsm->len < 0){                      //小于0 错误，判断errno 是真错还是假错
                if(errno == EAGAIN)
                    fsm->state = STATE_R;               // 假错，再去读
                else{
                    fsm->errstr = "read()";
                    fsm->state = STATE_Ex;              //真错，去往异常终止
                }
                    
            }
            else{
                fsm->pos = 0;
                fsm->state = STATE_W;                    //读到数据，跳到写态
            }
            break;
        case STATE_W:
            ret = write(fsm->dfd,fsm->buf+fsm->pos,fsm->len);  
            if(ret < 0){
                if (errno == EAGAIN)
                    fsm->state = STATE_W;
                else {
                    fsm->errstr = "write()";
                    fsm->state = STATE_Ex;
                }                   
            }
            else{
                fsm-> pos += ret;
                fsm->len -= ret;     //判断当前是否写够了len个字符 
                if(fsm->len  == 0)
                    fsm->state = STATE_R;
                else
                    fsm->state = STATE_W;
            }
            break;
        case STATE_Ex:
            perror(fsm->errstr);
            fsm->state = STATE_T;   //异常处理的结果肯定是将状态推向终止态
            break;
        case STATE_T:          
            /*do somethine*/
            break;
        default:
        /*do somethine*/
            abort();
            break;

    }
}
int max(int a,int b ){
    return a>b?a:b;
}
static int relay(int fd1, int fd2){
    //确保两个设备的文件描述符都是非阻塞状态
    int fd1_save,fd2_save;                   //存储文件状态
    fd1_save = fcntl(fd1,F_GETFL);           //存储当前的文件状态
    fcntl(fd1,F_SETFL,fd1_save|O_NONBLOCK);  //或上非阻塞，设置文件的状态
    fd2_save = fcntl(fd2,F_GETFL);           
    fcntl(fd2,F_SETFL,fd2_save|O_NONBLOCK);  

    //设置文件描述符集
    struct pollfd pfd[2];
    struct fsm_st  fsm12,fsm21;            //读左写右  和 读右写左的两个现场
    //*********初始化***********//
    fsm12.state = STATE_R;
    fsm12.sfd = fd1;
    fsm12.dfd = fd2;
    fsm21.state = STATE_R;
    fsm21.sfd = fd2;
    fsm21.dfd = fd1;

    pfd[0].fd = fd1;
    pfd[1].fd = fd2;
     
    while(fsm12.state != STATE_T ||fsm21.state != STATE_T ){  //当两个状态机都不是终止状态时，驱动开始
        //布置监视任务
        pfd[0].events = 0;   //位图清零  清零任务
        if(fsm12.state == STATE_R)  //A状态机是可读（读入A）的，但A现在能不能读啊？
            pfd[0].events |= POLLIN;//  读是关心的状态，写进去
        if(fsm21.state == STATE_W)  //B状态机是可写（向A写）的，但A现在能不能写；
            pfd[0].events |= POLLOUT  // 写是关心的状态 写进去

        pfd[1].events = 0;
        if(fsm12.state == STATE_W)   //A状态机是可写的，（向B写），B现在能不能写 
            pfd[1].events |= POLLOUT  // 状态机12是可写的，说明可以写到dfd上，进行监视
        if(fsm21.state == STATE_R)
            pfd[1].events |= POLLIN

        //监视
        if(fsm12.state < STATE_AUTO || fsm21.state < STATE_AUTO){   //只有在读态或写态
       
            while(poll(pfd,2,-1)<0){
                if(errno == EINTR)
                    continue;   //由于三个集被改变了 所以需要 绕大圈，重新布置监视任务
                perror("poll()");
                exit(1);
            }
        }
        //查看监视结果      根据监视结果有条件的推动状态机
        if( pfd[0].revents & POLLIN||pfd[1].revents & POLLOUT /         //  1可读 2可写 推动状态机12
        ||fsm12.state >STATE_AUTO){   //如果处于异常态，无条件推动
            fsm_driver(&fsm12);   //传指针
        }
        if( pfd[0].events & POLLOUT||pfd[1].revents & POLLIN /   //  2可读 1可写 推动状态机21
           ||fsm21.state > STATE_AUTO){
            fsm_driver(&fsm21);

        }
        
        
        
        
    }

    fcntl(fd1,F_SETFL,fd1_save);     //保证进模块和出模块状态不变，恢复原来的读写状态
    fcntl(fd2,F_SETFL,fd2_save);


}
//events 事件
//maxevents  可以连续取事件出来，在这里设置连续取的个数
//timeout  与前面一样的超时设置 -1代表阻塞 0代表非阻塞



int main (){

    //打开设备
    int fd1,fd2;    //文件描述符
    fd1 = open(TTY1,O_RDWR);
    if(fd1 < 0 ){
        perror("open ()");
        exit (1);
    }
    write(fd1,"TTY1\n",5);

    fd2 = open(TTY1,O_RDWR|O_NONBLOCK);
    if(fd2 < 0 ){
        perror("open ()");
        exit (1);
    }

    write(fd2,"TTY2\n",5);
    relay(fd1,fd2);  

    close(fd1);
    close(fd2);
    exit(0);
    
}